﻿namespace net.zxteam.zxlang.Lexer.ParserAssemblyGenerator.Internal.ParserAssemblyGeneratorImpl.Nemerle
{
	using System;
	using System.IO;
	using System.Linq;
	using System.Reflection;
	using System.Text;
	using System.Xml;

	internal sealed class NemerleParserAssemblyGeneratorFactory : ParserAssemblyGeneratorFactory
	{
		private class MyLogger : Microsoft.Build.Framework.ILogger
		{
			private readonly StringBuilder _sb = new StringBuilder();

			public void Initialize(Microsoft.Build.Framework.IEventSource eventSource)
			{
				eventSource.ErrorRaised += eventSource_ErrorRaised;
			}

			void eventSource_ErrorRaised(object sender, Microsoft.Build.Framework.BuildErrorEventArgs e)
			{
				_sb.AppendLine(e.Message);
			}

			public string Parameters { get; set; }

			public void Shutdown() { }

			public Microsoft.Build.Framework.LoggerVerbosity Verbosity
			{
				get { return Microsoft.Build.Framework.LoggerVerbosity.Normal; }
				set { }
			}

			public string Message { get { return _sb.ToString(); } }

			public override string ToString()
			{
				return _sb.ToString();
			}
		}

		public override IParserAssemblyGenerator CreateParserGenerator()
		{
			return new ParserGenerator();
		}

		private class ParserGenerator : ParserAssemblyGeneratorBase, IParserAssemblyGenerator
		{
			public override Assembly Generate(XmlReader pegXmlReader)
			{
#warning Hard code direcrory
				//string workDirectory = @"C:\Users\maxim.anurin\Work\zxlang\Lexer\CSharp\Temp\Build";
				string workDirectory = @"C:\Users\anurinm\Work\zxlang\Lexer\CSharp\Temp\Build";

				if (!Directory.Exists(workDirectory))
				{
					Directory.CreateDirectory(workDirectory);
				}

				CleanDirectory(workDirectory);

				string projectFile = Path.Combine(workDirectory, "project.csproj");
				string grammarClassesFile = Path.Combine(workDirectory, "grammar.n");
				string implementationClassesFile = Path.Combine(workDirectory, "implementation.n");

#warning ОПТИМИЗИРОВАТЬ СОЗДАНИЕ КЛОНОВ ВХОДЯЩЕГО РИДЕРА
				using (MemoryStream pegDocumentStream = new MemoryStream())
				{
					XmlDocument doc = new XmlDocument();
					doc.Load(pegXmlReader);
					doc.Save(pegDocumentStream);

					pegDocumentStream.Seek(0, SeekOrigin.Begin);
					using (XmlReader reader = XmlReader.Create(pegDocumentStream))
					using (FileStream stream = new FileStream(projectFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
					{
						Xsl.ProjectXslTransform.Instance.Transform(reader, stream);
						stream.Close();
					}

					pegDocumentStream.Seek(0, SeekOrigin.Begin);
					using (XmlReader reader = XmlReader.Create(pegDocumentStream))
					using (FileStream stream = new FileStream(grammarClassesFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
					{
						Xsl.GrammarXslTransform.Instance.Transform(reader, stream);
						stream.Close();
					}

					pegDocumentStream.Seek(0, SeekOrigin.Begin);
					using (XmlReader reader = XmlReader.Create(pegDocumentStream))
					using (FileStream stream = new FileStream(implementationClassesFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
					{
						Xsl.ImplemetationtXslTransform.Instance.Transform(reader, stream);
						stream.Close();
					}
				}

				//System.Environment.Exit(1);

				Microsoft.Build.Evaluation.Project msbuildProject
					= new Microsoft.Build.Evaluation.Project(projectFile);

				var logger = new MyLogger();
				if (msbuildProject.Build(logger))
				{
					var assemblyName = msbuildProject.Properties.Where(w => w.Name == "AssemblyName").First().EvaluatedValue;
					string assemblyFile = Path.Combine(workDirectory, "bin", assemblyName + ".dll");
					return AppDomain.CurrentDomain.Load(File.ReadAllBytes(assemblyFile));
				}

				throw new ApplicationException("Project was not build.\r\n" + logger.ToString());
			}

			private static void CleanDirectory(string directoryToClean)
			{
				if (directoryToClean == null) throw new ArgumentNullException();

				CleanDirectory(new DirectoryInfo(directoryToClean));
			}
			private static void CleanDirectory(DirectoryInfo directoryToClean)
			{
				if (directoryToClean == null) throw new ArgumentNullException();

				if (directoryToClean.Exists)
				{
					foreach (FileInfo file in directoryToClean.GetFiles())
					{
						file.Delete();
					}
					foreach (DirectoryInfo dir in directoryToClean.GetDirectories())
					{
						dir.Delete(true);
					}
				}
			}
		}
	}
}
